package com.arkisv.common.util;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.Security;
import java.util.Arrays;

/**
 * AES256加密、解密
 *
 * @author Feng King
 * @create 2017-01-18 11:35
 */
public class AESUtil {

    /**
     * 加密：对一个字符串利用key进行AES-256加密，最终结果进行Base64加密
     *
     * @return String Base64 and AES encoded String
     * @author Feng King
     * @create 2017/1/18 12:09
     */
    public static String encode(String key, String data) throws NullPointerException {
        if (key.length() != 32) {
            throw new NullPointerException("Key length not 32");
        }
        if (data == null || data.length() == 0) {
            throw new NullPointerException("Please give text");
        }
        try {
            //SecretKeySpec skeySpec = getKey(keyString);
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
            byte[] clearText = data.getBytes("UTF-8");
            /*final byte[] iv = new byte[16];
            Arrays.fill(iv, (byte) 0x00);
            System.out.println("iv : " + Arrays.toString(iv) + " , String : " + new String(iv));*/
            IvParameterSpec ivParameterSpec = new IvParameterSpec(key.substring(16).getBytes("UTF-8"));
            // 这个地方调用BouncyCastleProvider 让java支持PKCS7Padding
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            // Cipher is not thread safe
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
            return new String(Base64.encodeBase64(cipher.doFinal(clearText)), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 解密：对一个字符串使用key进行Base64 和 AES-256解密
     *
     * @author Feng King
     * @create 2017/1/18 12:11
     */
    public static String decode(String key, String text) throws NullPointerException {
        if (key == null || key.length() == 0) {
            throw new NullPointerException("Please give Password");
        }

        if (text == null || text.length() == 0) {
            throw new NullPointerException("Please give text");
        }
        try {
            //SecretKey sKey = getKey(password);

            // IMPORTANT TO GET SAME RESULTS ON iOS and ANDROID
            /*final byte[] iv = new byte[16];
            Arrays.fill(iv, (byte) 0x00);*/
            IvParameterSpec ivParameterSpec = new IvParameterSpec(key.substring(16).getBytes("UTF-8"));

            //byte[] encrypedPwdBytes = Base64.decode(text, Base64.DEFAULT);
            byte[] encrypedPwdBytes = Base64.decodeBase64(text.getBytes("UTF-8"));
            // cipher is not thread safe

            // 这个地方调用BouncyCastleProvider 让java支持PKCS7Padding
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getBytes(), "AES"), ivParameterSpec);
            byte[] decrypedValueBytes = (cipher.doFinal(encrypedPwdBytes));
            return new String(decrypedValueBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * Generates a SecretKeySpec for given password
     *
     * @param password
     * @return SecretKeySpec
     * @throws UnsupportedEncodingException
     */
    private static SecretKeySpec getKey(String password) throws UnsupportedEncodingException {
        // You can change it to 128 if you wish
        int keyLength = 256;
        byte[] keyBytes = new byte[keyLength / 8];
        // explicitly fill with zeros
        Arrays.fill(keyBytes, (byte) 0x0);
        // if password is shorter then key length, it will be zero-padded
        // to key length
        byte[] passwordBytes = password.getBytes("UTF-8");
        int length = passwordBytes.length < keyBytes.length ? passwordBytes.length : keyBytes.length;
        System.arraycopy(passwordBytes, 0, keyBytes, 0, length);
        return new SecretKeySpec(keyBytes, "AES");
    }

    public static void main(String args[]) throws UnsupportedEncodingException {
        long startTime = System.currentTimeMillis();
        //String old = UUIDUtil.getUUID2UpperCase()+System.currentTimeMillis()+"GET/api/v1/test/mail";
        String old = "5E4A8D8012C544CB9B65285FD10F04271484793021281GET/api/v1/test/mail";
        String key = "41422321AC0A9849FDCA2904A750A45E";
        System.out.println("原文：" + old + " , 长度：" + old.length());
        System.out.println("钥匙：" + key + " , 长度：" + key.length());
        System.out.println("偏移: " + key.substring(16) + " , 长度：" + key.substring(16).length());
        String encodeStr = AESUtil.encode(key, old);
        long encodeTime = System.currentTimeMillis();
        System.out.println("加密: " + encodeStr + " , 长度：" + encodeStr.length());
        String decodeStr = AESUtil.decode(key, encodeStr);
        System.out.println("解密: " + decodeStr + " , 长度：" + decodeStr.length());
        long decodeTime = System.currentTimeMillis();
        System.out.println("加密用时：" + (encodeTime - startTime) + " , 解密用时：" + (decodeTime - encodeTime));
    }
}
